-
Notifications
You must be signed in to change notification settings - Fork 12.8k
Fix type keyword completions #32474
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Fix type keyword completions #32474
Conversation
1. In functions, type keywords were omitted. 2. In All context, no keywords were omitted. (1) fixes #28737 (2) removes 17 keywords that should not be suggested, even at the toplevel of a typescript file: * private * protected * public * static * abstract * as * constructor * get * infer * is * namespace * require * set * type * from * global * of I don't know whether we have a bug tracking this or not.
Unless somebody knows how to un-update a submodule, I'm going to leave prettier in there. It's a result of |
@@ -19,7 +19,7 @@ verify.completions( | |||
includes: ["async", "await"].map( | |||
name => ({ name, sortText: completion.SortText.GlobalsOrKeywords }) | |||
), | |||
excludes: ["public", "private", "protected", "constructor", "readonly", "static", "abstract", "get", "set"], | |||
excludes: ["public", "private", "protected", "constructor", "static", "abstract", "get", "set"], |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why is readonly
ok in at position 1 and 2 ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
we allow now type keywords there and readonly is a type keyword with things like readonly T[]
src/harness/fourslash.ts
Outdated
"async", | ||
"await", | ||
"boolean", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why? I don't think we should be showing these here
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think you want to check if
let m = new Map<string/**/
is isPossiblyTypeArgumentPosition and add type keywords + function value keywords
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As far as I can tell, there's no more precise context than "inside a function" for completions. I think if we show things like class, var, interface
there then type keywords make sense too.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
don't think so. class, var, interface
can really be used in the function context to start local declarations? why would you want types?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
filterGlobalCompletion
has code that looks like it tries to do this, but it relies exclusively on isTypeOnlyCompletion
. Is that a good place to add a check for isPossiblyTypeArgumentPosition, do you think?
Instead of changing FunctionLikeBodyKeywords
OK, the PR is updated with my better understanding of KeywordCompletionFilters.TypeKeywords, which is now set when the context is possibly a type argument. I'm going to see if the two variants of |
Looks like No, because the distinction allows us to continue to suggest values when something might be a type argument (that is, when the brackets are unclosed), and to only suggest types when something is definitely a type argument (when the brackets are correctly matched). |
"bigint", | ||
"of", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is awesome. What was the change that actually made all these disappear from globals though?
src/services/completions.ts
Outdated
@@ -1182,7 +1182,7 @@ namespace ts.Completions { | |||
function filterGlobalCompletion(symbols: Symbol[]): void { | |||
const isTypeOnly = isTypeOnlyCompletion(); | |||
const allowTypes = isTypeOnly || !isContextTokenValueLocation(contextToken) && isPossiblyTypeArgumentPosition(contextToken, sourceFile, typeChecker); | |||
if (isTypeOnly) { | |||
if (allowTypes) { | |||
keywordFilters = isTypeAssertion() | |||
? KeywordCompletionFilters.TypeAssertionKeywords | |||
: KeywordCompletionFilters.TypeKeywords; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
note that this means we no longer offer other keywords in completions; new, this, typeof
are the useful ones I saw from scanning the list. Probably this
and typeof
should be added to the list typeKeywords
. Suggesting new
isn't very useful since it only saves 3 characters (or less).
For example,
return x < this.property;
will not suggest this
any more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Probably
this
andtypeof
should be added to the listtypeKeywords
.
Agreed. That seems objectively correct, since those are valid in a type position.
But why wouldn’t we show both type and non-type keywords here, given the uncertainty? Related questions:
- Are we trying to avoid semantic work here? It seems possible to differentiate between an
x
that can’t possibly accept type parameters (so we’re definitely not in a type position) vs. anx
that’s generic (so we may or may not be). - If we are trying to avoid semantic work, would it be reasonable to use whitespace as a heuristic?
Map <string, number>
seems extraordinarily uncommon, andx<3
fairly uncommon too.
I’m not sure whether these are practical or not, but I don’t think they’re necessary for this PR—seems like an improvement already. Just food for thought. (I do think adding this
and typeof
now is probably worth it, though.)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm. Both the simple "show all" idea and "be smarter about possible type arguments" idea are good.
For the second, isPossiblyTypeArgumentPosition already calls getTypeAtLocation
, so it's just a matter of querying the returned value.
I added this
and typeof
as types and it broke find all refs of global this
horribly. I haven't looked at why, but it means that I'll probably keep them where they are for now.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yep, isPossiblyTypeArgumentPosition
already is smarter about possible type arguments. My tests just don't show it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In any case, my original example, x < this.property
, is wrong -- it does suggest this
. It just doesn't when the identifier to the left of <
is a generic signature.
So I think making this
and typeof
type keywords is best fixed in a separate PR, since it requires more knowledge of services than I have now, and only affects things that are genuinely type contexts. I filed a bug #32486 to track it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah, that sounds better. I was a little concerned that you’d be missing those any time you were in the RHS of a less-than arithmetic comparison. 👍
@@ -2060,16 +2060,18 @@ namespace ts.Completions { | |||
case KeywordCompletionFilters.None: | |||
return false; | |||
case KeywordCompletionFilters.All: | |||
return kind === SyntaxKind.AsyncKeyword || SyntaxKind.AwaitKeyword || !isContextualKeyword(kind) && !isClassMemberCompletionKeyword(kind) || kind === SyntaxKind.DeclareKeyword || kind === SyntaxKind.ModuleKeyword |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@andrewbranch Here! SyntaxKind.AwaitKeyword
isn't compared ===kind
and since it's truthy it caused .All
to let all keywords through.
Does this fix #27955? (it's okay if it doesn't, but just close it if it does) |
@DanielRosenwasser pretty sure it can't possibly, because Also, there are some JS-specific checks that happen. They might avoid this fix entirely; I haven't tested that. |
src/services/completions.ts
Outdated
@@ -1182,7 +1182,7 @@ namespace ts.Completions { | |||
function filterGlobalCompletion(symbols: Symbol[]): void { | |||
const isTypeOnly = isTypeOnlyCompletion(); | |||
const allowTypes = isTypeOnly || !isContextTokenValueLocation(contextToken) && isPossiblyTypeArgumentPosition(contextToken, sourceFile, typeChecker); | |||
if (isTypeOnly) { | |||
if (allowTypes) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think this is correct change. KeywordFilters = KeywordCompletionFilters.TypeKeywords
would only offer type keywords? but that's not what you want here. I think you want isTypeOnly => KeywordCompletionFilters.TypeKeywords
, otherwise if you have allowTypes
then you want global + type keywords and rest of the time only global keywords ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
After some discussion, we decided that isPossiblyTypeArgumentPosition is smart enough to rely on only providing types when it's true.
This simplifies code that relies on isPossiblyTypeArgumentPosition, as well as some tests. We no longer fall back to types+values, instead suggesting one or the other. isPossiblyTypeArgumentPosition should have a very low false positive rate since it relies on types, which means that people could see values-only completions a little more than before. The most common failure case I can think of is when a variable that should have a generic signature type actually has type any
. I think this is acceptable.
Because isPossiblyTypeArgumentPosition doesn't give false positives now that it uses type information.
(1) fixes #28737
(2) removes 17 keywords that should not be suggested, even at the
toplevel of a typescript file:
I don't know whether we have a bug tracking this or not.